package xyz.lwm.lazycat.utility.scan;


import lombok.Getter;
import xyz.lwm.lazycat.utility.Assert;
import xyz.lwm.lazycat.utility.StringUtil;
import xyz.lwm.lazycat.utility.resource.Location;

import java.util.Arrays;

import static xyz.lwm.lazycat.utility.scan.Scanner.CHAR;
import static xyz.lwm.lazycat.utility.scan.Scanner.CLASSPATH_PREFIX;
import static xyz.lwm.lazycat.utility.scan.Scanner.DOT;
import static xyz.lwm.lazycat.utility.scan.Scanner.FILE_PREFIX;
import static xyz.lwm.lazycat.utility.scan.Scanner.SLASH;
import static xyz.lwm.lazycat.utility.scan.Scanner.WILDCARD;


/**
 * location and pattern
 *
 * @author lwm
 */
@Getter
public class LocationPattern {

    // prefix: file:, classpath:
    private final Location location;

    // location, used to scan the directory
    private final String path;

    // pattern, used to match the file
    private final String pattern;

    private LocationPattern(Location location, String path, String pattern) {
        this.location = location;
        this.path = path;
        this.pattern = pattern;
    }

    /**
     * parse source path to location pattern .
     *
     * @param source classpath:app/a.txt   file:D:\temp\a.txt
     * @return pattern
     */
    public static LocationPattern parse(String source) {
        Location location;
        String locationPattern;

        if (source.startsWith(FILE_PREFIX)) {
            location = Location.FILEPATH;
            locationPattern = source.substring(FILE_PREFIX.length());

        } else if (source.startsWith(CLASSPATH_PREFIX)) {
            location = Location.CLASSPATH;
            locationPattern = source.substring(CLASSPATH_PREFIX.length());

        } else {
            String msg = StringUtil.format(
                    "Unsupported source: `{}`, it must start with `{}` or `{}`",
                    source, FILE_PREFIX, CLASSPATH_PREFIX);
            throw new IllegalArgumentException(msg);
        }

        locationPattern = locationPattern.trim().replaceAll("\\\\+", String.valueOf(SLASH));
        // check pattern, the pattern which matches all files is not allowed.
        checkValid(locationPattern);

        return getLocationPattern(location, locationPattern);
    }

    // 获取 locationPattern 不包含通配符部分的最长路径, 这样使得扫描时缩小范围
    private static LocationPattern getLocationPattern(Location location, String locationPattern) {
        // if exact location, such as: xx.txt, xx/test.txt, xx/test.txt.gz
        if (isExactLocation(locationPattern)) {
            return new LocationPattern(location, locationPattern, locationPattern);
        }

        // the dot index
        int dotIndex = locationPattern.indexOf(DOT);
        // the wildcard index
        int wildcardIndex = locationPattern.indexOf(WILDCARD);
        // get one char index
        int charIndex = locationPattern.indexOf(CHAR);

        // get main index
        int index = getMinValidIndex(dotIndex, wildcardIndex, charIndex);

        // get slash index, it could split the location and pattern.
        int slashIndex = index < 0 ? locationPattern.length() : locationPattern.lastIndexOf(SLASH, index);

        // no slash, such as: xx.txt, xx*/test*.txt, xx.*txt, xx/test.**txt
        if (slashIndex < 0) {
            // scan all
            return new LocationPattern(location, "", locationPattern);
        }
        return new LocationPattern(location, locationPattern.substring(0, slashIndex), locationPattern);
    }

    // 获取最小的有效索引, 用于截取扫描范围
    private static int getMinValidIndex(int... indexes) {
        return Arrays.stream(indexes).filter(i -> i != -1).min().orElse(-1);
    }

    // 全量扫描并全部收集, 此处不允许
    private static void checkValid(String locationPattern) {
        char[] array = locationPattern.toCharArray();
        int count = 0;
        for (char c : array) {
            switch (c) {
                case WILDCARD:
                case CHAR:
                case DOT:
                case SLASH:
                    count++;
            }
        }
        Assert.isTrue(count < array.length, "Pattern matches all, it's not allowed.");
    }

    // 是否是精确路径
    private static boolean isExactLocation(String pattern) {
        // no * , no ? .
        return !pattern.contains(String.valueOf(WILDCARD)) &&
                !pattern.contains(String.valueOf(CHAR));
    }

}
